#include "grflib_stdafx.h"
#include "../include/IAStage.h"
#include "../include/Hash.h"
#include "../include/D3D10Engine.h"
#include "../include/PointerCast.h"

namespace grflib { namespace engine { namespace core {

CIAStage::CIAStage()
{
}

CIAStage::~CIAStage()
{
    OnDestroy();
}

HRESULT CIAStage::SetInputLayout(std::size_t VSSignature,
	BYTE *pVSByteCode,
	std::size_t VSByteCodeSize, 
	const std::vector<D3D10_INPUT_ELEMENT_DESC> &elements)
{
	HRESULT hr;

	ID3D10Device *pd3dDevice = CEngine::Instance().GetD3DDevice();
	//hashing D3D10_INPUT_ELEMENT_DESC and Vertex shader
	std::size_t key = boost::hash_range(elements.begin(), elements.end());
	boost::hash_combine(key, VSSignature);

	//lookup existing layouts
	boost::shared_ptr<ID3D10InputLayout> pLayout = m_layouts.FindObj(key);
	
	if (!pLayout) //doesn't exist, so create
	{
        ID3D10InputLayout *pNewLayout = 0;
		V_RETURN( pd3dDevice->CreateInputLayout(&elements[0], elements.size(),
												pVSByteCode, VSByteCodeSize,
												&pNewLayout) );
        pLayout = grflib::engine::core::cast::GetCOMPtr(pNewLayout);
		m_layouts.AddObj(key, pLayout);
		m_pCurrentLayout = pLayout;
	}
	else if (pLayout != m_pCurrentLayout) //exists, set as current layout
	{
		m_pCurrentLayout = pLayout;
	}
	else //same as the current layout, do nothing
	{
	}
	pd3dDevice->IASetInputLayout(pLayout.get());

	return S_OK;
}

void CIAStage::SetVertexBuffers(UINT startSlot, 
	UINT nBuffer, 
	ID3D10Buffer *const *ppVB,
	const UINT *pStrides,
	const UINT *pOffsets)
{
	CEngine::Instance().GetD3DDevice()->IASetVertexBuffers(startSlot, nBuffer, ppVB, pStrides, pOffsets);
}

void CIAStage::SetVertexBuffer(UINT startSlot, 
	ID3D10Buffer *pVB,
	UINT stride,
	UINT offset)
{
	SetVertexBuffers(startSlot, 1, &pVB, &stride, &offset);
}

void CIAStage::SetIndexBuffer(ID3D10Buffer *pIndexBuffer,
	DXGI_FORMAT format,
	UINT offset)
{
	CEngine::Instance().GetD3DDevice()->IASetIndexBuffer(pIndexBuffer, format, offset);
}

void CIAStage::SetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY topology)
{
	CEngine::Instance().GetD3DDevice()->IASetPrimitiveTopology(topology);
}

void CIAStage::OnDestroy()
{
    m_layouts.Clear();
    m_pCurrentLayout.reset();
}

}}} //namespaces